﻿Imports System.Runtime.InteropServices
Imports System.IO
Imports System.Xml.Serialization
Imports System.ComponentModel

Public Module Interception

	Public Enum InterceptionMouseState
		INTERCEPTION_MOUSE_LEFT_BUTTON_DOWN = &H1
		INTERCEPTION_MOUSE_LEFT_BUTTON_UP = &H2
		INTERCEPTION_MOUSE_RIGHT_BUTTON_DOWN = &H4
		INTERCEPTION_MOUSE_RIGHT_BUTTON_UP = &H8
		INTERCEPTION_MOUSE_MIDDLE_BUTTON_DOWN = &H10
		INTERCEPTION_MOUSE_MIDDLE_BUTTON_UP = &H20

		INTERCEPTION_MOUSE_BUTTON_1_DOWN = INTERCEPTION_MOUSE_LEFT_BUTTON_DOWN
		INTERCEPTION_MOUSE_BUTTON_1_UP = INTERCEPTION_MOUSE_LEFT_BUTTON_UP
		INTERCEPTION_MOUSE_BUTTON_2_DOWN = INTERCEPTION_MOUSE_RIGHT_BUTTON_DOWN
		INTERCEPTION_MOUSE_BUTTON_2_UP = INTERCEPTION_MOUSE_RIGHT_BUTTON_UP
		INTERCEPTION_MOUSE_BUTTON_3_DOWN = INTERCEPTION_MOUSE_MIDDLE_BUTTON_DOWN
		INTERCEPTION_MOUSE_BUTTON_3_UP = INTERCEPTION_MOUSE_MIDDLE_BUTTON_UP

		INTERCEPTION_MOUSE_BUTTON_4_DOWN = &H40
		INTERCEPTION_MOUSE_BUTTON_4_UP = &H80
		INTERCEPTION_MOUSE_BUTTON_5_DOWN = &H100
		INTERCEPTION_MOUSE_BUTTON_5_UP = &H200

		INTERCEPTION_MOUSE_WHEEL = &H400
		INTERCEPTION_MOUSE_HWHEEL = &H800
	End Enum

	Public Enum InterceptionFilterMouseState
		INTERCEPTION_FILTER_MOUSE_NONE = &H0
		INTERCEPTION_FILTER_MOUSE_ALL = &HFFFF

		INTERCEPTION_FILTER_MOUSE_LEFT_BUTTON_DOWN = InterceptionMouseState.INTERCEPTION_MOUSE_LEFT_BUTTON_DOWN
		INTERCEPTION_FILTER_MOUSE_LEFT_BUTTON_UP = InterceptionMouseState.INTERCEPTION_MOUSE_LEFT_BUTTON_UP
		INTERCEPTION_FILTER_MOUSE_RIGHT_BUTTON_DOWN = InterceptionMouseState.INTERCEPTION_MOUSE_RIGHT_BUTTON_DOWN
		INTERCEPTION_FILTER_MOUSE_RIGHT_BUTTON_UP = InterceptionMouseState.INTERCEPTION_MOUSE_RIGHT_BUTTON_UP
		INTERCEPTION_FILTER_MOUSE_MIDDLE_BUTTON_DOWN = InterceptionMouseState.INTERCEPTION_MOUSE_MIDDLE_BUTTON_DOWN
		INTERCEPTION_FILTER_MOUSE_MIDDLE_BUTTON_UP = InterceptionMouseState.INTERCEPTION_MOUSE_MIDDLE_BUTTON_UP

		INTERCEPTION_FILTER_MOUSE_BUTTON_1_DOWN = InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_1_DOWN
		INTERCEPTION_FILTER_MOUSE_BUTTON_1_UP = InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_1_UP
		INTERCEPTION_FILTER_MOUSE_BUTTON_2_DOWN = InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_2_DOWN
		INTERCEPTION_FILTER_MOUSE_BUTTON_2_UP = InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_2_UP
		INTERCEPTION_FILTER_MOUSE_BUTTON_3_DOWN = InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_3_DOWN
		INTERCEPTION_FILTER_MOUSE_BUTTON_3_UP = InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_3_UP

		INTERCEPTION_FILTER_MOUSE_BUTTON_4_DOWN = InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_4_DOWN
		INTERCEPTION_FILTER_MOUSE_BUTTON_4_UP = InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_4_UP
		INTERCEPTION_FILTER_MOUSE_BUTTON_5_DOWN = InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_5_DOWN
		INTERCEPTION_FILTER_MOUSE_BUTTON_5_UP = InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_5_UP

		INTERCEPTION_FILTER_MOUSE_WHEEL = InterceptionMouseState.INTERCEPTION_MOUSE_WHEEL
		INTERCEPTION_FILTER_MOUSE_HWHEEL = InterceptionMouseState.INTERCEPTION_MOUSE_HWHEEL

		INTERCEPTION_FILTER_MOUSE_MOVE = &H1000
	End Enum

	Public Enum InterceptionMouseFlag
		INTERCEPTION_MOUSE_MOVE_RELATIVE = &H0
		INTERCEPTION_MOUSE_MOVE_ABSOLUTE = &H1
		INTERCEPTION_MOUSE_VIRTUAL_DESKTOP = &H2
		INTERCEPTION_MOUSE_ATTRIBUTES_CHANGED = &H4
		INTERCEPTION_MOUSE_MOVE_NOCOALESCE = &H8
		INTERCEPTION_MOUSE_TERMSRV_SRC_SHADOW = &H100
	End Enum

	Public Structure InterceptionMouseStroke
		'unsigned short state;
		Public state As System.UInt16
		'unsigned short flags;
		Public flags As System.UInt16
		'short rolling;
		Public rolling As System.Int16
		'int x;
		Public x As System.Int32
		'int y;
		Public y As System.Int32
		'unsigned int information; 
		Public information As System.UInt32
	End Structure



	Public Enum InterceptionKeyState As UShort
		INTERCEPTION_KEY_DOWN = &H0
		INTERCEPTION_KEY_UP = &H1
		INTERCEPTION_KEY_E0 = &H2
		INTERCEPTION_KEY_E1 = &H4
		INTERCEPTION_KEY_TERMSRV_SET_LED = &H8
		INTERCEPTION_KEY_TERMSRV_SHADOW = &H10
		INTERCEPTION_KEY_TERMSRV_VKPACKET = &H20
	End Enum

	Public Enum InterceptionFilterKeyState
		INTERCEPTION_FILTER_KEY_NONE = &H0
		INTERCEPTION_FILTER_KEY_ALL = &HFFFF
		INTERCEPTION_FILTER_KEY_DOWN = InterceptionKeyState.INTERCEPTION_KEY_UP
		INTERCEPTION_FILTER_KEY_UP = InterceptionKeyState.INTERCEPTION_KEY_UP << 1
		INTERCEPTION_FILTER_KEY_E0 = InterceptionKeyState.INTERCEPTION_KEY_E0 << 1
		INTERCEPTION_FILTER_KEY_E1 = InterceptionKeyState.INTERCEPTION_KEY_E1 << 1
		INTERCEPTION_FILTER_KEY_TERMSRV_SET_LED = InterceptionKeyState.INTERCEPTION_KEY_TERMSRV_SET_LED << 1
		INTERCEPTION_FILTER_KEY_TERMSRV_SHADOW = InterceptionKeyState.INTERCEPTION_KEY_TERMSRV_SHADOW << 1
		INTERCEPTION_FILTER_KEY_TERMSRV_VKPACKET = InterceptionKeyState.INTERCEPTION_KEY_TERMSRV_VKPACKET << 1
	End Enum

	Public Structure InterceptionKeyStroke
		'unsigned short code;
		Public code As System.UInt16
		'unsigned short state;
		'Public state As System.UInt16
		Public state As InterceptionKeyState
		'unsigned int information;
		Public information As System.UInt32

		Public Shared Operator =(stroke1 As InterceptionKeyStroke, stroke2 As InterceptionKeyStroke)
			Return (stroke1.code = stroke2.code) AndAlso (stroke1.state = stroke2.state)
		End Operator

		Public Shared Operator <>(stroke1 As InterceptionKeyStroke, stroke2 As InterceptionKeyStroke)
			Return (stroke1.code <> stroke2.code) OrElse (stroke1.state <> stroke2.state)
		End Operator
	End Structure

	Public Class InterceptionWrapper
		'InterceptionContext INTERCEPTION_API interception_create_context(void);
		<DllImport("interception.dll", EntryPoint:="interception_create_context", CallingConvention:=CallingConvention.Cdecl)>
		Public Shared Function interception_create_context() As IntPtr
		End Function

		'void INTERCEPTION_API interception_destroy_context(InterceptionContext context);
		<DllImport("interception.dll", EntryPoint:="interception_destroy_context", CallingConvention:=CallingConvention.Cdecl)>
		Public Shared Sub interception_destroy_context(InterceptionContext As IntPtr)
		End Sub

		'InterceptionPrecedence INTERCEPTION_API interception_get_precedence(InterceptionContext context, InterceptionDevice device);
		<DllImport("interception.dll", EntryPoint:="interception_get_precedence", CallingConvention:=CallingConvention.Cdecl)>
		Public Shared Function interception_get_precedence(InterceptionContext As IntPtr, InterceptionDevice As System.Int32) As System.Int32
		End Function

		'void INTERCEPTION_API interception_set_precedence(InterceptionContext context, InterceptionDevice device, InterceptionPrecedence precedence);
		<DllImport("interception.dll", EntryPoint:="interception_set_precedence", CallingConvention:=CallingConvention.Cdecl)>
		Public Shared Sub interception_set_precedence(InterceptionContext As IntPtr, InterceptionDevice As System.Int32, InterceptionPrecedence As System.Int32)
		End Sub

		'InterceptionFilter INTERCEPTION_API interception_get_filter(InterceptionContext context, InterceptionDevice device);
		<DllImport("interception.dll", EntryPoint:="interception_get_filter", CallingConvention:=CallingConvention.Cdecl)>
		Public Shared Function interception_get_filter(InterceptionContext As IntPtr, InterceptionDevice As System.Int32) As System.UInt16
		End Function

		'void INTERCEPTION_API interception_set_filter(InterceptionContext context, InterceptionPredicate predicate, InterceptionFilter filter);
		<DllImport("interception.dll", EntryPoint:="interception_set_filter", CallingConvention:=CallingConvention.Cdecl)>
		Public Shared Sub interception_set_filter(InterceptionContext As IntPtr, InterceptionPredicate As IntPtr, InterceptionFilter As System.UInt16)
		End Sub

		'InterceptionDevice INTERCEPTION_API interception_wait(InterceptionContext context);
		<DllImport("interception.dll", EntryPoint:="interception_wait", CallingConvention:=CallingConvention.Cdecl)>
		Public Shared Function interception_wait(InterceptionContext As IntPtr) As System.Int32
		End Function

		'InterceptionDevice INTERCEPTION_API interception_wait_with_timeout(InterceptionContext context, unsigned long milliseconds);
		<DllImport("interception.dll", EntryPoint:="interception_wait_with_timeout", CallingConvention:=CallingConvention.Cdecl)>
		Public Shared Function interception_wait_with_timeout(InterceptionContext As IntPtr, miliseconds As System.UInt32) As System.Int32
		End Function

		'int INTERCEPTION_API interception_send(InterceptionContext context, InterceptionDevice device, const InterceptionStroke *stroke, unsigned int nstroke);
		<DllImport("interception.dll", EntryPoint:="interception_send", CallingConvention:=CallingConvention.Cdecl)>
		Public Shared Function interception_send(InterceptionContext As IntPtr, InterceptionDevice As System.Int32, InterceptionStroke As IntPtr, nstroke As System.UInt32) As System.Int32
		End Function

		'int INTERCEPTION_API interception_receive(InterceptionContext context, InterceptionDevice device, InterceptionStroke *stroke, unsigned int nstroke);
		<DllImport("interception.dll", EntryPoint:="interception_receive", CallingConvention:=CallingConvention.Cdecl)>
		Public Shared Function interception_receive(InterceptionContext As IntPtr, InterceptionDevice As System.Int32, InterceptionStroke As IntPtr, nstroke As System.UInt32) As System.Int32
		End Function

		'unsigned int INTERCEPTION_API interception_get_hardware_id(InterceptionContext context, InterceptionDevice device, void *hardware_id_buffer, unsigned int buffer_size);
		<DllImport("interception.dll", EntryPoint:="interception_get_hardware_id", CallingConvention:=CallingConvention.Cdecl)>
		Public Shared Function interception_get_hardware_id(InterceptionContext As IntPtr, InterceptionDevice As System.Int32, hardware_id_buffer As IntPtr, buffer_size As System.UInt32) As System.UInt32
		End Function

		'int INTERCEPTION_API interception_is_invalid(InterceptionDevice device);
		<DllImport("interception.dll", EntryPoint:="interception_is_invalid", CallingConvention:=CallingConvention.Cdecl)>
		Public Shared Function interception_is_invalid(InterceptionDevice As System.Int32) As System.Int32
		End Function

		'int INTERCEPTION_API interception_is_keyboard(InterceptionDevice device);
		<DllImport("interception.dll", EntryPoint:="interception_is_keyboard", CallingConvention:=CallingConvention.Cdecl)>
		Public Shared Function interception_is_keyboard(InterceptionDevice As System.Int32) As System.Int32
		End Function

		'int INTERCEPTION_API interception_is_mouse(InterceptionDevice device);
		<DllImport("interception.dll", EntryPoint:="interception_is_mouse", CallingConvention:=CallingConvention.Cdecl)>
		Public Shared Function interception_is_mouse(InterceptionDevice As System.Int32) As System.Int32
		End Function
	End Class




	<DllImport("kernel32")>
	Public Function LoadLibrary(DllName As String) As IntPtr
	End Function

	<DllImport("kernel32")>
	Public Function FreeLibrary(DllHandle As IntPtr) As System.Boolean
	End Function

	<DllImport("kernel32")>
	Public Function GetProcAddress(hDll As IntPtr, ProcName As String) As IntPtr
	End Function

	' nastavuje se z Form1
	Public RightModifierKeysEqualsLeft As Boolean = True
	Public StartKeySequenceWithModifierKeys As Boolean = True
	Public Logging As Boolean = False

	' slovniky pro prevod Key na Code
	Public dictKeyCode As New Dictionary(Of String, UShort)
	Public dictCodeKey As New Dictionary(Of UShort, String)
	Public dictE0KeyCode As New Dictionary(Of String, UShort)
	Public dictE0CodeKey As New Dictionary(Of UShort, String)
	Public dictE1KeyCode As New Dictionary(Of String, UShort)
	Public dictE1CodeKey As New Dictionary(Of UShort, String)
	Public KeyCode As New TKeyCode
	Public DefinicePoradiKeyStroku As New List(Of InterceptionKeyStroke)
	Public ModifierKeys As New List(Of InterceptionKeyStroke)
	Public dictRightLeftModifier As New Dictionary(Of InterceptionKeyStroke, InterceptionKeyStroke)

	Public Class TKeyCode ' pomocna trida pro nacteni obsahu XML
		Public Property Key As New List(Of String)
		Public Property Code As New List(Of UShort)
		Public Property E0Key As New List(Of String)
		Public Property E0Code As New List(Of UShort)
		Public Property E1Key As New List(Of String)
		Public Property E1Code As New List(Of UShort)
	End Class

	Public Sub InicializujSlovniky()
		Dim FilePath As String = AppDomain.CurrentDomain.BaseDirectory & "KeyCode.xml"
		KeyCode = NactiTriduZXML(FilePath, New TKeyCode)
		If KeyCode Is Nothing Then
			MsgBox("KeyCode.xml is not found. Program exits.")
			Environment.Exit(0)
		End If
		NaplnDictionary(KeyCode.Key, KeyCode.Code, dictKeyCode, dictCodeKey)
		NaplnDictionary(KeyCode.E0Key, KeyCode.E0Code, dictE0KeyCode, dictE0CodeKey)
		NaplnDictionary(KeyCode.E1Key, KeyCode.E1Code, dictE1KeyCode, dictE1CodeKey)

		With DefinicePoradiKeyStroku
			.Clear()
			.Add(VytvorKeyStroke("L CTRL"))
			.Add(VytvorKeyStroke("L ALT"))
			.Add(VytvorKeyStroke("L SHFT"))
			.Add(VytvorKeyStroke("L GUI"))
			.Add(VytvorKeyStroke("R CTRL"))
			.Add(VytvorKeyStroke("R ALT"))
			.Add(VytvorKeyStroke("R SHFT"))
			.Add(VytvorKeyStroke("R GUI"))
		End With

		With ModifierKeys
			.Clear()
			.Add(VytvorKeyStroke("L CTRL"))
			.Add(VytvorKeyStroke("L ALT"))
			.Add(VytvorKeyStroke("L SHFT"))
			.Add(VytvorKeyStroke("L GUI"))
			.Add(VytvorKeyStroke("R CTRL"))
			.Add(VytvorKeyStroke("R ALT"))
			.Add(VytvorKeyStroke("R SHFT"))
			.Add(VytvorKeyStroke("R GUI"))
		End With

		With dictRightLeftModifier
			.Clear()
			.Add(VytvorKeyStroke("R CTRL"), VytvorKeyStroke("L CTRL"))
			.Add(VytvorKeyStroke("R ALT"), VytvorKeyStroke("L ALT"))
			.Add(VytvorKeyStroke("R SHFT"), VytvorKeyStroke("L SHFT"))
			.Add(VytvorKeyStroke("R GUI"), VytvorKeyStroke("L GUI"))
		End With
	End Sub

	Public Sub NaplnDictionary(ByRef Keys As List(Of String), ByRef Codes As List(Of UShort), ByRef dictKeyCode As Dictionary(Of String, UShort), ByRef dictCodeKey As Dictionary(Of UShort, String))
		For n = 0 To Keys.Count - 1
			dictKeyCode.Add(Keys(n), Codes(n))
			dictCodeKey.Add(Codes(n), Keys(n))
		Next
	End Sub

	Public Function DejKey(KeyStroke As InterceptionKeyStroke) As String
		Dim Key As String = Nothing
		If (KeyStroke.state And InterceptionKeyState.INTERCEPTION_KEY_E0) = InterceptionKeyState.INTERCEPTION_KEY_E0 Then
			dictE0CodeKey.TryGetValue(KeyStroke.code, Key)
		ElseIf (KeyStroke.state And InterceptionKeyState.INTERCEPTION_KEY_E1) = InterceptionKeyState.INTERCEPTION_KEY_E1 Then
			dictE1CodeKey.TryGetValue(KeyStroke.code, Key)
		Else
			dictCodeKey.TryGetValue(KeyStroke.code, Key)
		End If

		If Key Is Nothing Then
			Key = "??undefined??"
		End If
		Return Key
	End Function

	Public Function DejCode(Key As String) As UShort
		Dim code As UShort = 0
		If dictKeyCode.TryGetValue(Key, code) Then
		ElseIf dictE0KeyCode.TryGetValue(Key, code) Then
		ElseIf dictE1KeyCode.TryGetValue(Key, code) Then
		Else
			Throw New Exception("Requested KEY doesn't exists in dictionaries.")
		End If
		Return code
	End Function

	Public Function VytvorKeyStroke(Key As String) As InterceptionKeyStroke
		Dim KeyStroke As New InterceptionKeyStroke

		If dictKeyCode.TryGetValue(Key, KeyStroke.code) Then
			KeyStroke.state = InterceptionKeyState.INTERCEPTION_KEY_DOWN
		ElseIf dictE0KeyCode.TryGetValue(Key, KeyStroke.code) Then
			KeyStroke.state = InterceptionKeyState.INTERCEPTION_KEY_E0
		ElseIf dictE1KeyCode.TryGetValue(Key, KeyStroke.code) Then
			KeyStroke.state = InterceptionKeyState.INTERCEPTION_KEY_E1
		Else
			Throw New Exception("Requested KEY doesn't exist in dictionaries.")
		End If

		Return KeyStroke
	End Function

	Public Function VytvorKeyStroke(code As UShort, state As InterceptionKeyState) As InterceptionKeyStroke
		Return New InterceptionKeyStroke With {.code = code, .state = state}
	End Function


	Public Function DejRetezecZeSeznamuStroku(SeznamStroku As List(Of Interception.InterceptionKeyStroke)) As String
		Dim shlp As String = ""
		If SeznamStroku IsNot Nothing Then
			' vytvor string, kterej prijde do DGV
			For Each stroke As InterceptionKeyStroke In SeznamStroku
				shlp = shlp & DejKey(stroke) & " + "
			Next
			shlp = shlp.Substring(0, shlp.Length - 3)
		End If
		Return shlp
	End Function

	Public Function UpravPoradiStrokuu(lstStroke As List(Of Interception.InterceptionKeyStroke)) As List(Of Interception.InterceptionKeyStroke)
		Dim hlplst As New List(Of Interception.InterceptionKeyStroke)

		For Each ks In DefinicePoradiKeyStroku
			Dim ind As Integer = lstStroke.FindIndex(Function(x) x = ks)
			If ind = -1 Then Continue For

			hlplst.Add(lstStroke(ind))	' pridej do noveho seznamu
			lstStroke.RemoveAt(ind)		' odstran v puvodnim seznamu
		Next
		' pripoj zbytek puvodniho seznamu
		hlplst.AddRange(lstStroke)

		Return hlplst
	End Function

	Public Function ZmenPraveModifierNaLeveAOdstranDuplicity(lstStroke As List(Of InterceptionKeyStroke)) As List(Of InterceptionKeyStroke)
		Dim LeftModifier As InterceptionKeyStroke

		For n = lstStroke.Count - 1 To 0 Step -1
			If Not dictRightLeftModifier.TryGetValue(lstStroke(n), LeftModifier) Then Continue For
			' podivej se, jestli uz neni left stroke (ktery se dela z right stroku) v seznamu
			If lstStroke.Contains(LeftModifier) Then
				lstStroke.RemoveAt(n)
			Else
				lstStroke(n) = LeftModifier
			End If
		Next

		Return lstStroke
	End Function

	Public Function UpravSeznamStrokuu(lstStroke As List(Of InterceptionKeyStroke)) As List(Of InterceptionKeyStroke)
		' pokud je vyzadovano, uprav poradi a modifier keys a podle teto upravene vstupni sekvence dohledej sekvenci vystupni
		If RightModifierKeysEqualsLeft Then
			lstStroke = ZmenPraveModifierNaLeveAOdstranDuplicity(lstStroke)
		End If
		If StartKeySequenceWithModifierKeys Then
			lstStroke = UpravPoradiStrokuu(lstStroke)
		End If

		Return lstStroke
	End Function

	Public Function DejDoplnekREALdoSYSTEM(lstREAL As List(Of InterceptionKeyStroke), lstSYSTEM As List(Of InterceptionKeyStroke)) As List(Of InterceptionKeyStroke)
		Dim hlplst As New List(Of InterceptionKeyStroke)

		For Each r In lstREAL
			If Not lstSYSTEM.Contains(r) Then
				hlplst.Add(r)
			End If
		Next
		Return hlplst
	End Function

	Function DejOutput(IntKeyb As InterceptedKeyboard, lstStroke As List(Of InterceptionKeyStroke)) As List(Of List(Of InterceptionKeyStroke))
		Dim Output As New List(Of List(Of InterceptionKeyStroke))

		For i = 0 To IntKeyb.Inputs.Count - 1
			Dim lstInput As List(Of InterceptionKeyStroke) = IntKeyb.Inputs(i).ToList

			If Not (lstInput.Count = lstStroke.Count) Then Continue For

			Dim bFlag As Boolean = False
			For n = 0 To lstInput.Count - 1
				If Not (lstInput(n) = lstStroke(n)) Then
					bFlag = True
					Exit For
				End If
			Next

			If bFlag Then Continue For
			Output = IntKeyb.Outputs(i).ToList
			Exit For
		Next

		Return Output
	End Function

	Public Class InterceptedKeyboard
		Public Property RealPressedKeys As New List(Of InterceptionKeyStroke)
		Public Property SystemPressedKeys As New List(Of InterceptionKeyStroke)
		Public Property Inputs As New List(Of List(Of InterceptionKeyStroke))
		Public Property Outputs As New List(Of List(Of List(Of InterceptionKeyStroke)))
	End Class


End Module
